package com.opennews.openplatform.service;

import com.opennews.openplatform.dto.journey.CreateJourneyDto;
import com.opennews.openplatform.dto.journey.UpdateJourneyDto;
import com.opennews.openplatform.entity.tables.records.JourneyRecord;
import com.opennews.openplatform.jooq.DSLContextWrapper;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.jooq.DSLContext;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

import static com.opennews.openplatform.entity.Tables.*;

@RequiredArgsConstructor
@Service
public class JourneyService {
    private final DSLContext dslContext;
    private final DSLContextWrapper dslContextWrapper;
    private final RegionService regionService;

    /**
     * Queries all journeys which associated with provided account group.
     *
     * @return The list of map instance which contains keys below.
     * id: String of the journey id.
     * startDate: Start date of the journey.
     * endDate: End date of the journey.
     * regionId: String of the region id.
     * regionName: String of the name of the region.
     * regionTitle: String of the title of the region.
     * regionFlagUrl: String of the flag url of the region.
     */
    public List<Map<String, Object>> queryListByAccountGroup(String accountGroupId, int pageSize, int pageIndex) {
        return dslContext.select(
                        JOURNEY.ID.as("id"),
                        JOURNEY.START_DATE.as("startDate"),
                        JOURNEY.END_DATE.as("endDate"),
                        REGION.ID.as("regionId"),
                        REGION.NAME.as("regionName"),
                        REGION.TITLE.as("regionTitle")
                )
                .from(JOURNEY)
                .join(REGION).on(JOURNEY.REGION_ID.eq(REGION.ID))
                .where(JOURNEY.ACCOUNT_GROUP_ID.eq(accountGroupId))
                .orderBy(JOURNEY.START_DATE.desc())
                .limit(pageSize)
                .offset(pageIndex * pageSize)
                .fetch()
                .map(p -> {
                    Map<String, Object> item = p.intoMap();
                    item.put("regionFlagUrl", regionService.getFlagUrl(item.get("regionName").toString()));

                    return item;
                });
    }

    /**
     * Adds new journey for account group.
     *
     * @param accountGroupId: The string of user account group id.
     * @param data:           The map which contains keys with same name as journey properties.
     */
    public void add(String accountGroupId, @Valid CreateJourneyDto data) {
        // Creates new Journey with posted data.
        JourneyRecord record = dslContext.newRecord(JOURNEY);
        record.from(data);
        record.setAccountGroupId(accountGroupId);
        record.setRegionId(data.getRegion().getId());
        record.insert();
    }

    /**
     * Updates journey info based on provided map data and its id.
     * The map keys must match journey property names.
     * If the map item will be ignored if its value is null.
     *
     * @param accountGroupId: The string of user account group id.
     * @param data            The map which contains keys with same name as journey properties.
     *                        The map must contains key of id.
     */
    public void update(String accountGroupId, @Valid UpdateJourneyDto data) {
        dslContext.update(JOURNEY)
                .set(JOURNEY.START_DATE, data.getStartDate())
                .set(JOURNEY.END_DATE, data.getEndDate())
                .where(
                        JOURNEY.ACCOUNT_GROUP_ID.eq(accountGroupId)
                                .and(JOURNEY.ID.eq(data.getId()))
                )
                .execute();
    }

    /**
     * Deletes the Journey with provided id.
     *
     * @param accountGroupId: The string of the user account group id.
     * @param id:             The journey id.
     */
    public boolean delete(String accountGroupId, String id) {
        boolean isDeleted = false;

        // Queries the input id and makes sure it belongs to the specific account group.
        // This is an example shows how to secure the data by taking extra action.
        // This can void other account group users call api directly and provide id which they don't actually own it.
        // AKA parallel authentication.
        // Checks if this journey contains any income/expense.
        if (dslContextWrapper.count(JOURNEY_INCOME_EXPENSE_DETAIL, JOURNEY_INCOME_EXPENSE_DETAIL.ACCOUNT_GROUP_ID.eq(accountGroupId)
                .and(JOURNEY_INCOME_EXPENSE_DETAIL.JOURNEY_ID.eq(id))) == 0) {
            // If this journey contains no income/expense then deletes it.
            dslContext.deleteFrom(JOURNEY)
                    .where(JOURNEY.ACCOUNT_GROUP_ID.eq(accountGroupId)
                            .and(JOURNEY.ID.eq(id)))
                    .execute();

            isDeleted = true;
        }

        return isDeleted;
    }
}
